import path from 'path'
import os from 'os'
import download from '@serverless/utils/download'
import fse from 'fs-extra'
import { promises as fsp } from 'fs'
import untildify from 'untildify'
import { renameService } from './rename-service.js'
import ServerlessError from '../serverless-error.js'
import dirExists from './fs/dir-exists.js'
import safeMoveFile from './fs/safe-move-file.js'

const resolveServiceName = (serviceDir) => {
  let serviceName = path
    .basename(serviceDir)
    .toLowerCase()
    .replace(/[^0-9a-z.]+/g, '-')
  if (!serviceName.match(/^[a-z]/)) serviceName = `service-${serviceName}`
  return serviceName
}

async function downloadTemplateFromExamples({
  template,
  name,
  path: projectPath,
  isLegacy,
}) {
  const downloadUrl = 'https://github.com/serverless/examples/archive/v3.zip'
  let pathToDirectory
  if (isLegacy) {
    pathToDirectory = `legacy/${template}`
  } else {
    pathToDirectory = template
  }

  const downloadServicePath = path.join(os.tmpdir(), 'examples')

  const serviceDir = projectPath
    ? path.resolve(untildify(projectPath))
    : process.cwd()
  const serviceName = name || resolveServiceName(serviceDir)

  // We do not want to run this check if project should be setup in current directory
  if (serviceDir !== process.cwd() && (await dirExists(serviceDir))) {
    const errorMessage = [
      `The directory "${serviceDir}" already exists, and serverless will not overwrite it. `,
      'Rename or move the directory and try again if you want serverless to create it"',
    ].join('')
    throw new ServerlessError(errorMessage, 'TARGET_FOLDER_ALREADY_EXISTS')
  }

  const downloadOptions = {
    timeout: 30000,
    extract: true,
    strip: 1,
    mode: '755',
  }

  try {
    await download(downloadUrl, downloadServicePath, downloadOptions)
  } catch (err) {
    throw new ServerlessError(
      `Could not download template. Ensure that you are using the latest version of Serverless Framework: ${err.message}`,
      'TEMPLATE_DOWNLOAD_FAILED',
    )
  }

  // Examples repo has all examples nested in directories
  const directory = path.join(downloadServicePath, pathToDirectory)

  try {
    if (serviceDir === process.cwd()) {
      // ensure no template file already exists in current directory that we may overwrite
      const topLevelContentList = await fsp.readdir(directory)
      await Promise.all(
        topLevelContentList.map(async (f) => {
          let exists
          try {
            await fsp.access(path.join(process.cwd(), f))
            exists = true
          } catch (err) {
            // Ignore, file does not exist
          }
          if (exists) {
            const errorMessage = [
              `The file or directory "${f}" already exists, and serverless will not overwrite it. `,
              `Move it and try again if you want serverless to write a new "${f}"`,
            ].join('')
            throw new ServerlessError(
              errorMessage,
              'TEMPLATE_FILE_ALREADY_EXISTS',
            )
          }
        }),
      )

      await Promise.all(
        topLevelContentList.map(async (f) => {
          await safeMoveFile(path.join(directory, f), path.join(serviceDir, f))
        }),
      )
    } else {
      await safeMoveFile(directory, serviceDir)
    }
  } catch (err) {
    if (err.code === 'ENOENT') {
      throw new ServerlessError(
        'Could not find provided template. Ensure that the template provided with "--template" exists.',
        'INVALID_TEMPLATE',
      )
    }

    if (err.code === 'EACCESS') {
      const errorMessage = [
        'Error unable to create a service in this directory. ',
        'Please check that you have the required permissions to write to the directory',
      ].join('')

      throw new ServerlessError(errorMessage, 'UNABLE_TO_CREATE_SERVICE')
    }

    throw err
  }

  // Cleanup whole downloaded dir
  await fse.remove(downloadServicePath)

  if (template !== 'plugin') {
    renameService(serviceName, serviceDir)
  }
  return serviceName
}

export default downloadTemplateFromExamples
